/*
 * Copyright (c) 2017-present, CV4J Contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cv4j.core.spatial.conv;

import com.cv4j.core.datamodel.ColorProcessor;
import com.cv4j.core.datamodel.ImageProcessor;
import com.cv4j.core.filters.BaseFilter;

import java.util.Optional;

import static com.cv4j.image.util.Tools.clamp;

/**
 * 找到边缘滤光片
 *
 * @author dev
 * @date 2021/07/22
 */
public class FindEdgeFilter extends BaseFilter {
    /**
     * The horizontal Sobel operator filter is used for edge detection
     * <p>
     * This is a 3x3 filter:<br>
     * -1 -2 -1 <br>
     * 0  0  0 <br>
     * 1  2  1 <br>
     */
    public static final int[] SOBEL_X = new int[]{-1, -2, -1, 0, 0, 0, 1, 2, 1};
    /**
     * Vertical Sobel operator filters for edge detection
     * <p>
     * This is a 3x3 filter:<br>
     * -1  0  1 <br>
     * -2  0  2 <br>
     * -1  0  1 <br>
     */
    public static final int[] SOBEL_Y = new int[]{-1, 0, 1, -2, 0, 2, -1, 0, 1};

    /**
     * 做的过滤器
     *
     * @param src src
     * @return {@link Optional<ImageProcessor>}
     */
    @Override
    public Optional<ImageProcessor> doFilter(ImageProcessor src) {
        int total = width * height;
        byte[][] output = new byte[3][total];

        int offset = 0;
        int x0 = SOBEL_X[0];
        int x1 = SOBEL_X[1];
        int x2 = SOBEL_X[2];
        int x3 = SOBEL_X[3];
        int x4 = SOBEL_X[4];
        int x5 = SOBEL_X[5];
        int x6 = SOBEL_X[6];
        int x7 = SOBEL_X[7];
        int x8 = SOBEL_X[8];

        int k0 = SOBEL_Y[0];
        int k1 = SOBEL_Y[1];
        int k2 = SOBEL_Y[2];
        int k3 = SOBEL_Y[3];
        int k4 = SOBEL_Y[4];
        int k5 = SOBEL_Y[5];
        int k6 = SOBEL_Y[6];
        int k7 = SOBEL_Y[7];
        int k8 = SOBEL_Y[8];

        int yr = 0;
        int yg = 0;
        int yb = 0;
        int xr = 0;
        int xg = 0;
        int xb = 0;
        int r1 = 0;
        int g1 = 0;
        int b1 = 0;
        for (int row = 1; row < height - 1; row++) {
            offset = row * width;
            for (int col = 1; col < width - 1; col++) {
                // red
                yr = k0 * (RED[offset - width + col - 1] & 0xff)
                    + k1 * (RED[offset - width + col] & 0xff)
                    + k2 * (RED[offset - width + col + 1] & 0xff)
                    + k3 * (RED[offset + col - 1] & 0xff)
                    + k4 * (RED[offset + col] & 0xff)
                    + k5 * (RED[offset + col + 1] & 0xff)
                    + k6 * (RED[offset + width + col - 1] & 0xff)
                    + k7 * (RED[offset + width + col] & 0xff)
                    + k8 * (RED[offset + width + col + 1] & 0xff);

                xr = x0 * (RED[offset - width + col - 1] & 0xff)
                    + x1 * (RED[offset - width + col] & 0xff)
                    + x2 * (RED[offset - width + col + 1] & 0xff)
                    + x3 * (RED[offset + col - 1] & 0xff)
                    + x4 * (RED[offset + col] & 0xff)
                    + x5 * (RED[offset + col + 1] & 0xff)
                    + x6 * (RED[offset + width + col - 1] & 0xff)
                    + x7 * (RED[offset + width + col] & 0xff)
                    + x8 * (RED[offset + width + col + 1] & 0xff);

                // green
                yg = k0 * (GREED[offset - width + col - 1] & 0xff)
                    + k1 * (GREED[offset - width + col] & 0xff)
                    + k2 * (GREED[offset - width + col + 1] & 0xff)
                    + k3 * (GREED[offset + col - 1] & 0xff)
                    + k4 * (GREED[offset + col] & 0xff)
                    + k5 * (GREED[offset + col + 1] & 0xff)
                    + k6 * (GREED[offset + width + col - 1] & 0xff)
                    + k7 * (GREED[offset + width + col] & 0xff)
                    + k8 * (GREED[offset + width + col + 1] & 0xff);

                xg = x0 * (GREED[offset - width + col - 1] & 0xff)
                    + x1 * (GREED[offset - width + col] & 0xff)
                    + x2 * (GREED[offset - width + col + 1] & 0xff)
                    + x3 * (GREED[offset + col - 1] & 0xff)
                    + x4 * (GREED[offset + col] & 0xff)
                    + x5 * (GREED[offset + col + 1] & 0xff)
                    + x6 * (GREED[offset + width + col - 1] & 0xff)
                    + x7 * (GREED[offset + width + col] & 0xff)
                    + x8 * (GREED[offset + width + col + 1] & 0xff);
                // blue
                yb = k0 * (BLUE[offset - width + col - 1] & 0xff)
                    + k1 * (BLUE[offset - width + col] & 0xff)
                    + k2 * (BLUE[offset - width + col + 1] & 0xff)
                    + k3 * (BLUE[offset + col - 1] & 0xff)
                    + k4 * (BLUE[offset + col] & 0xff)
                    + k5 * (BLUE[offset + col + 1] & 0xff)
                    + k6 * (BLUE[offset + width + col - 1] & 0xff)
                    + k7 * (BLUE[offset + width + col] & 0xff)
                    + k8 * (BLUE[offset + width + col + 1] & 0xff);

                xb = x0 * (BLUE[offset - width + col - 1] & 0xff)
                    + x1 * (BLUE[offset - width + col] & 0xff)
                    + x2 * (BLUE[offset - width + col + 1] & 0xff)
                    + x3 * (BLUE[offset + col - 1] & 0xff)
                    + x4 * (BLUE[offset + col] & 0xff)
                    + x5 * (BLUE[offset + col + 1] & 0xff)
                    + x6 * (BLUE[offset + width + col - 1] & 0xff)
                    + x7 * (BLUE[offset + width + col] & 0xff)
                    + x8 * (BLUE[offset + width + col + 1] & 0xff);

                // magnitude
                r1 = (int) Math.sqrt(yr * yr + xr * xr);
                g1 = (int) Math.sqrt(yg * yg + xg * xg);
                b1 = (int) Math.sqrt(yb * yb + xb * xb);

                // find edges
                output[0][offset + col] = (byte) clamp(r1);
                output[1][offset + col] = (byte) clamp(g1);
                output[2][offset + col] = (byte) clamp(b1);

                double dy = (yr + yg + yb);
                double dx = (xr + xg + xb);
                double theta = Math.atan(dy / dx);

                // for next pixel
                yr = 0;
                yg = 0;
                yb = 0;
                xr = 0;
                xg = 0;
                xb = 0;
            }
        }
        ((ColorProcessor) src).putRGB(output[0], output[1], output[2]);
        output[0] = null;
        output[1] = null;
        output[2] = null;
        output = null;
        return Optional.ofNullable(src);
    }

}
